package com.yuedou.api.util.positionjudge;


import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 判断是否在多边形内
 * 点在多边形内返回true, 否则返回false
 */
public class IsPtInPoly {

    /**
     * 判断点是否在多边形内
     *
     * @param point 检测点
     * @param pts   多边形的顶点
     * @return 点在多边形内返回true, 否则返回false
     */
    public static boolean isPtInPoly(Point2D point, List<Point2D> pts) {

        int N = pts.size();
        boolean boundOrVertex = true; //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
        int intersectCount = 0;//cross points count of x
        double precision = 2e-10; //浮点类型计算时候与0比较时候的容差
        Point2D p1, p2;//neighbour bound vertices
        Point2D p = point; //当前点

        p1 = pts.get(0);//left vertex
        for (int i = 1; i <= N; ++i) {//check all rays
            if (p.equals(p1)) {
                return boundOrVertex;//p is an vertex
            }

            p2 = pts.get(i % N);//right vertex
            if (p.x.doubleValue() < Math.min(p1.x.doubleValue(), p2.x.doubleValue()) || p.x.doubleValue() > Math.max(p1.x.doubleValue(), p2.x.doubleValue())) {//ray is outside of our interests
                p1 = p2;
                continue;//next ray left point
            }

            if (p.x.doubleValue() > Math.min(p1.x.doubleValue(), p2.x.doubleValue()) && p.x.doubleValue() < Math.max(p1.x.doubleValue(), p2.x.doubleValue())) {//ray is crossing over by the algorithm (common part of)
                if (p.y.doubleValue() <= Math.max(p1.y.doubleValue(), p2.y.doubleValue())) {//x is before of ray
                    if (p1.x.doubleValue() == p2.x.doubleValue() && p.y.doubleValue() >= Math.min(p1.y.doubleValue(), p2.y.doubleValue())) {//overlies on a horizontal ray
                        return boundOrVertex;
                    }

                    if (p1.y.doubleValue() == p2.y.doubleValue()) {//ray is vertical
                        if (p1.y.doubleValue() == p.y.doubleValue()) {//overlies on a vertical ray
                            return boundOrVertex;
                        } else {//before ray
                            ++intersectCount;
                        }
                    } else {//cross point on the left side
                        double xinters = (p.x.doubleValue() - p1.x.doubleValue()) * (p2.y.doubleValue() - p1.y.doubleValue()) / (p2.x.doubleValue() - p1.x.doubleValue()) + p1.y.doubleValue();//cross point of y
                        if (Math.abs(p.y.doubleValue() - xinters) < precision) {//overlies on a ray
                            return boundOrVertex;
                        }

                        if (p.y.doubleValue() < xinters) {//before ray
                            ++intersectCount;
                        }
                    }
                }
            } else {//special case when ray is crossing through the vertex
                if (p.x.doubleValue() == p2.x.doubleValue() && p.y.doubleValue() <= p2.y.doubleValue()) {//p crossing over p2
                    Point2D p3 = pts.get((i + 1) % N); //next vertex
                    if (p.x.doubleValue() >= Math.min(p1.x.doubleValue(), p3.x.doubleValue()) && p.x.doubleValue() <= Math.max(p1.x.doubleValue(), p3.x.doubleValue())) {//p.x lies between p1.x & p3.x
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;//next ray left point
        }

        if (intersectCount % 2 == 0) {//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }

    /**
     * 判断经纬度点是否在多边形范围内
     * @param latitude 经度
     * @param longitude 纬度
     * @param polygon 多边形的经纬度点集合
     * @return true表示在多边形内，false表示在多边形外
     */
    public static boolean isPointInPolygon(double latitude, double longitude, List<Point2D> polygon) {
        int intersectCount = 0;
        for (int i = 0; i < polygon.size() - 1; i++) {
            Point2D p1 = polygon.get(i);
            Point2D p2 = polygon.get(i + 1);
            // 判断经纬度点是否在多边形的边上
            if (latitude == p1.getY().doubleValue() && longitude == p1.getX().doubleValue()
                    || latitude == p2.getY().doubleValue() && longitude == p2.getX().doubleValue()) {
                return true;
            }
            // 判断经纬度点是否与多边形的边相交
            if (p1.getX().doubleValue() < longitude && p2.getX().doubleValue() >= longitude
                    || p2.getX().doubleValue() < longitude && p1.getX().doubleValue() >= longitude) {
                if (p1.getY().doubleValue() + (longitude - p1.getX().doubleValue())
                        / (p2.getX().doubleValue() - p1.getX().doubleValue())
                        * (p2.getY().doubleValue() - p1.getY().doubleValue()) < latitude) {
                    intersectCount++;
                }
            }
        }
        // 判断与射线相交的边的数量
        return intersectCount % 2 == 1;
    }

    /**
     * 判断是否在圆形内
     *
     * @param p
     * @param c
     * @return
     */
    public static boolean distencePC(Point2D p, Circle c) {//判断点与圆心之间的距离和圆半径的关系
        Boolean result;
        String s;
        double d2 = Math.hypot((p.getX().doubleValue() - c.getCc().getX().doubleValue()), (p.getY().doubleValue() - c.getCc().getY().doubleValue()));
        //System.out.println("d2==" + d2);
        double r = c.getR();

        if (d2 > r) {
            result=false;
            //s = "圆外";
        } else if (d2 < r) {
            result=true;
            //s = "圆内";
        } else {
            result=true;
            //s = "圆上";
        }
        return result;
    }


    public static List<Point2D> getPoint(JSONArray jsonArray){
        JSONObject jsonObject;
        List<Point2D> list = new ArrayList<>();
        Point2D aa;
        for (int i = 0; i < jsonArray.size(); i++) {
            jsonObject = JSONUtil.parseObj(jsonArray.get(i));
            aa = new Point2D(new BigDecimal(jsonObject.get("lng").toString()), new BigDecimal(jsonObject.get("lat").toString()));
            list.add(aa);
        }
        return list;
    }

    private static boolean judge(Point2D point2D, JSONArray jsonArray) {
        JSONObject jsonObject;
        List<Point2D> list = new ArrayList<>();
        Point2D aa;
        for (int i = 0; i < jsonArray.size(); i++) {
            jsonObject = JSONUtil.parseObj(jsonArray.get(i));
            aa = new Point2D(new BigDecimal(jsonObject.get("lng").toString()), new BigDecimal(jsonObject.get("lat").toString()));
            list.add(aa);
        }
        return IsPtInPoly.isPtInPoly(point2D, list);
    }


    public static void main(String[] args) {
//        119.5001080000	35.4127460000
        String points = "[" +
                "{\"lat\":35.475038,\"lng\":119.470987},{\"lat\":35.460837,\"lng\":119.56492}," +
                "{\"lat\":35.363193,\"lng\":119.52611},{\"lat\":35.365028,\"lng\":119.482236},{\"lat\":35.370991,\"lng\":119.441738}," +
                "{\"lat\":35.437927,\"lng\":119.447363},{\"lat\":35.474122,\"lng\":119.469862},{\"lat\":35.459921,\"lng\":119.565483}," +
                "{\"lat\":35.363193,\"lng\":119.525547},{\"lat\":35.370991,\"lng\":119.441738},{\"lat\":35.445717,\"lng\":119.44905}]";
        JSONArray jsonArray = JSONUtil.parseArray(points);
        Point2D point = new Point2D(new BigDecimal(119.493841),	new BigDecimal(35.418141));
//
//        // 测试一个点是否在多边形内
        List<Point2D> pts = new ArrayList<Point2D>();
        boolean bl = judge(point, jsonArray);

        List<Point2D> point2DList = getPoint(jsonArray);
        bl = isPointInPolygon(119.493841, 35.418141, point2DList);
//        pts.add(new Point2D(116.395, 39.910));
//        pts.add(new Point2D(116.394, 39.914));
//        pts.add(new Point2D(116.403, 39.920));
//        pts.add(new Point2D(116.402, 39.914));
//        pts.add(new Point2D(116.410, 39.913));
//
        if (bl) {
            System.out.println("点在多边形内");
        } else {
            System.out.println("点在多边形外");
        }
//
//        // 测试一个点是否在圆形内
//        Point2D centerPoint = new Point2D(116.404172, 39.916605);
//        Circle c = new Circle();
//        c.setCC(centerPoint);
//        c.setR(0.0056);
        //String s = distencePC(point, c);
        //System.out.println("点是否在圆内：" + s);
    }

}
